<#setting number_format="#">
<#macro buildFieldName field withoutPrefix><#if withoutPrefix || (field.prefix!"")?length == 0><#if (field.value!"")?contains(".")>${field.value!""}<#else>"${field.value!""}"</#if><#else>"${field.prefix!""}", <#if (field.value!"")?contains(".")>${field.value!""}<#else>"${field.value!""}"</#if></#if></#macro>
<#macro toSetId><#if multiPrimaryKey>.id(id)<#elseif primaryKey?? && !primaryKey.autoIncrement>.${primaryKey.name!"id"}(<#if primaryKey.config.createOrUpdate.enabled>${primaryKey.name}<#else>buildPrimaryKey()</#if>)</#if></#macro>
<#macro buildFieldCond p><#if p.config?? && p.config.query?? && p.config.query.enabled><#if p.config.query.like><#if p.config.query.validation?? && p.config.query.validation.dateTime?? && p.config.query.validation.dateTime.enabled><#else><#if p.field??>.exprNotEmpty(queryBean.get${p.name?cap_first}(), c -> c.and().likeWrap(<@buildFieldName p.field false/>).param(Like.create(queryBean.get${p.name?cap_first}()).contains()))</#if></#if><#else><#if p.config.query.validation?? && p.config.query.validation.dateTime?? && p.config.query.validation.dateTime.enabled>
                .expr(queryBean.getStart${p.name?cap_first}() != null || queryBean.getEnd${p.name?cap_first}() != null, c -> c.rangeWrap(<@buildFieldName p.field false/>, queryBean.getStart${p.name?cap_first}(), queryBean.getEnd${p.name?cap_first}(), Cond.LogicalOpt.AND))<#else><#if p.field??>
                .exprNotEmpty(queryBean.get${p.name?cap_first}(), c -> c.and().eqWrap(<@buildFieldName p.field false/>).param(queryBean.get${p.name?cap_first}()))</#if></#if></#if></#if></#macro>
/*
 * Copyright ${.now?string("yyyy")} the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ${app.packageName}.repository.impl;

<#if entityPackageName??>import ${entityPackageName}.*;<#elseif api.entityClass??>import ${api.entityClass};</#if>
import ${app.packageName}.bean.${api.name?cap_first}Bean;<#if !api.view>
import ${app.packageName}.bean.${api.name?cap_first}UpdateBean;<#if multiPrimaryKey>
import ${entityPackageName}.${api.name?cap_first}PK;<#else>
import net.ymate.platform.commons.util.UUIDUtils;</#if></#if>
import ${app.packageName}.repository.I${api.name?cap_first}Repository;
import ${app.packageName}.vo.${api.name?cap_first}VO;
import net.ymate.platform.commons.exception.DataVersionMismatchException;
import net.ymate.platform.core.persistence.Fields;
import net.ymate.platform.core.persistence.IResultSet;
import net.ymate.platform.core.persistence.Page;
import net.ymate.platform.core.persistence.Params;
import net.ymate.platform.core.persistence.annotation.Transaction;
import net.ymate.platform.persistence.jdbc.IDBLocker;
import net.ymate.platform.persistence.jdbc.IDatabase;
import net.ymate.platform.persistence.jdbc.base.impl.BatchUpdateOperator;
import net.ymate.platform.persistence.jdbc.query.*;
import net.ymate.platform.persistence.jdbc.repo.IRepository;
import net.ymate.platform.persistence.jdbc.repo.annotation.Repository;
import net.ymate.platform.persistence.jdbc.support.EntityStateWrapper;
import org.apache.commons.lang.NullArgumentException;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;

/**
 * <#if api.description?? && (api.description?length > 0)>${api.description}<br></#if>
 *
 * ${api.name?cap_first}Repository generated By CrudMojo on ${.now?string("yyyy/MM/dd a HH:mm")}
 *
 * @author ${app.author!"YMP (https://www.ymate.net/)"}
 * @version ${app.version!"1.0.0"}
 */
@Repository
public class ${api.name?cap_first}Repository implements I${api.name?cap_first}Repository, IRepository {

    <#if !api.view><#if multiPrimaryKey>public static ${api.name?cap_first}PK buildPrimaryKey(<#list nonAutoPrimaryFields as p>${p.type} ${p.name}<#if p_has_next>, </#if></#list>) {
        ${api.name?cap_first}PK.Builder builder = ${api.name?cap_first}PK.builder()<#list nonAutoPrimaryFields as p>
                .${p.name}(${p.name})</#list>;
        return builder.build();
    }<#elseif primaryKey?? && !primaryKey.autoIncrement>public static ${primaryKey.type} buildPrimaryKey() {
        <#if primaryKey.type?ends_with("String")>return UUIDUtils.UUID()<#else>// TODO Need to build the ${api.name?cap_first} primary key object.
        return null</#if>;
    }</#if>

    <#if !(api.settings??) || api.settings.enableCreate!true>@Override
    @Transaction
    public ${entityName} create${api.name?cap_first}(IDatabase owner, String dataSourceName, <#if multiPrimaryKey>${api.name?cap_first}PK id, <#elseif primaryKey?? && primaryKey.config.createOrUpdate.enabled>${primaryKey.type} ${primaryKey.name}, </#if>${api.name?cap_first}UpdateBean updateBean) throws Exception {<#if multiPrimaryKey || primaryKey.config.createOrUpdate.enabled>
        if (<#if multiPrimaryKey || !primaryKey.type?ends_with("String")><#if multiPrimaryKey>id<#else>${primaryKey.name}</#if> == null<#else>StringUtils.isBlank(<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>)</#if>) {
            throw new NullArgumentException("<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>");
        }</#if>
        if (updateBean != null) {<#if createTimeProp?? && !createTimeProp.foreign>
            Long now = System.currentTimeMillis();</#if>
            ${entityName} entity = ${entityName}.builder(owner).dataSourceName(dataSourceName)<@toSetId/><#list normalFields as p><#if p.config?? && p.config.createOrUpdate?? && p.config.createOrUpdate.enabled>
                    .${p.name}(updateBean.get${p.name?cap_first}())</#if></#list><#if createTimeProp?? && !createTimeProp.foreign>
                    .${createTimeProp.name}(now)<#if lastModifyTimeProp?? && !lastModifyTimeProp.foreign>
                    .${lastModifyTimeProp.name}(now)</#if></#if>
                    .build();
            return entity.save();
        }
        return null;
    }

    @Override
    @Transaction
    public ${entityName} update${api.name?cap_first}(IDatabase owner, String dataSourceName, <#if multiPrimaryKey>${api.name?cap_first}PK id<#else>${primaryKey.type} ${primaryKey.name}</#if>, ${api.name?cap_first}UpdateBean updateBean<#if lastModifyTimeProp?? && !lastModifyTimeProp.foreign>, ${lastModifyTimeProp.type} ${lastModifyTimeProp.name}</#if>) throws Exception {
        if (<#if multiPrimaryKey || !primaryKey.type?ends_with("String")><#if multiPrimaryKey>id<#else>${primaryKey.name}</#if> == null<#else>StringUtils.isBlank(<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>)</#if>) {
            throw new NullArgumentException("<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>");
        }
        if (updateBean != null) {
            ${entityName} entity = ${entityName}.builder(owner).dataSourceName(dataSourceName)<#if primaryKey??>.${primaryKey.name!"id"}<#elseif multiPrimaryKey>.id</#if>(<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>).build().load(IDBLocker.DEFAULT);
            if (entity != null) {<#if lastModifyTimeProp?? && !lastModifyTimeProp.foreign>
                DataVersionMismatchException.comparisonVersion(entity.get${lastModifyTimeProp.name?cap_first}(), ${lastModifyTimeProp.name});
                //</#if>
                EntityStateWrapper<${entityName}> stateWrapper = entity.stateWrapper();
                stateWrapper.getEntity().bind()<#list normalFields as p><#if p.config?? && p.config.createOrUpdate?? && p.config.createOrUpdate.enabled>
                        .${p.name}(updateBean.get${p.name?cap_first}())</#if></#list><#if lastModifyTimeProp?? && !lastModifyTimeProp.foreign>
                        .${lastModifyTimeProp.name}(System.currentTimeMillis())</#if>;
                return stateWrapper.update();
            }
        }
        return null;
    }

    @Override
    @Transaction
    public int update${api.name?cap_first}s(IDatabase owner, String dataSourceName, <#if multiPrimaryKey>${api.name?cap_first}PK[] ids<#else>${primaryKey.type}[] ${primaryKey.name}s</#if>, Fields fields, Params values) throws Exception {
        if (ArrayUtils.isEmpty(<#if multiPrimaryKey>ids<#else>${primaryKey.name}s</#if>)) {
            throw new NullArgumentException("<#if multiPrimaryKey>ids<#else>${primaryKey.name}s</#if>");
        }
        if (fields == null || fields.isEmpty()) {
            throw new NullArgumentException("fields");
        }
        if (values == null || values.isEmpty()) {
            throw new NullArgumentException("values");
        }
        Update update = Update.create(owner, dataSourceName, ${entityName}.class)
                .field(fields)
                .where(Cond.create(owner, dataSourceName)<#if multiPrimaryKey><#list primaryFields as p>
                        <#if (p_index > 0)>.and()</#if>.eqWrap(<@buildFieldName p.field true/>)</#list><#else>.eqWrap(<@buildFieldName primaryKey.field true/>)</#if>);
        BatchSQL batchSql = BatchSQL.create(update);
        Arrays.stream(<#if multiPrimaryKey>ids<#else>${primaryKey.name}s</#if>).map(id -> Params.create(values, <#if multiPrimaryKey><#list primaryFields as p>id.get${p.name?cap_first}()<#if p_has_next>, </#if></#list><#else>id</#if>)).forEachOrdered(batchSql::addParameter);
        return BatchUpdateOperator.parseEffectCounts(batchSql.execute(update.dataSourceName()));
    }</#if></#if>

    <#if !(api.settings??) || api.settings.enableQuery!true><#if !api.view>@Override
    public ${api.name?cap_first}VO query${api.name?cap_first}(IDatabase owner, String dataSourceName, <#if multiPrimaryKey>${api.name?cap_first}PK id<#else>${primaryKey.type} ${primaryKey.name}</#if>, Fields excludedFields) throws Exception {
        if (<#if multiPrimaryKey || !primaryKey.type?ends_with("String")><#if multiPrimaryKey>id<#else>${primaryKey.name}</#if> == null<#else>StringUtils.isBlank(<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>)</#if>) {
            throw new NullArgumentException("<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>");
        }
        Cond cond = Cond.create(owner, dataSourceName)<#if (primaryFields?size > 0)><#if multiPrimaryKey><#list primaryFields as p>
                .eqWrap(<@buildFieldName p.field false/>).param(<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>.get${p.name?cap_first}())</#list><#else>.eqWrap(<@buildFieldName primaryKey.field false/>).param(<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>)</#if></#if>;
        return Query.build(owner, dataSourceName, ${api.name?cap_first}VO.class).where(cond.buildWhere(), true)
                .addExcludeField(excludedFields)
                .findFirst();
    }</#if>

    @Override
    public IResultSet<${api.name?cap_first}VO> query${api.name?cap_first}s(IDatabase owner, String dataSourceName, ${api.name?cap_first}Bean queryBean, Cond otherCond, OrderBy orderBy, Fields excludedFields, Page page) throws Exception {
<#--        // ${entityName}.FieldConditionBuilder conditionBuilder = ${entityName}.conditionBuilder(owner, dataSourceName, "a");-->
<#--        // Cond cond = Cond.create(owner, dataSourceName).eqOne()<#list normalFields as p><#if p.config?? && p.config.query?? && p.config.query.enabled && !p.config.query.like><#if p.config.query.validation?? && p.config.query.validation.dateTime?? && p.config.query.validation.dateTime.enabled>-->
<#--        //      .expr(queryBean.getStart${p.name?cap_first}() != null || queryBean.getEnd${p.name?cap_first}() != null, c -> c.and(conditionBuilder.${p.name}.rangeWrap(queryBean.getStart${p.name?cap_first}(), queryBean.getEnd${p.name?cap_first}())))</#if></#if></#list><#list normalFields as p><#if p.config?? && p.config.query?? && p.config.query.enabled && !p.config.query.like><#if p.config.query.validation?? && p.config.query.validation.dateTime?? && p.config.query.validation.dateTime.enabled><#else><#if p.field??>-->
<#--        //      .exprNotEmpty(queryBean.get${p.name?cap_first}(), c -> c.and(conditionBuilder.${p.name}.eqWrapValue(queryBean.get${p.name?cap_first}())))</#if></#if></#if></#list><#list normalFields as p><#if p.config?? && p.config.query?? && p.config.query.enabled && p.config.query.like><#if p.config.query.validation?? && p.config.query.validation.dateTime?? && p.config.query.validation.dateTime.enabled><#else><#if p.field??>-->
<#--        //      .exprNotEmpty(queryBean.get${p.name?cap_first}(), c -> c.and(conditionBuilder.${p.name}.likeWrap(Like.create(queryBean.get${p.name?cap_first}()).full())))</#if></#if></#if></#list>;-->
        Cond cond = Cond.create(owner, dataSourceName);
        if (queryBean != null) {
            cond.eqOne()<#if multiPrimaryKey><#list primaryFields as p><#if p.config?? && p.config.query?? && p.config.query.enabled><@buildFieldCond p/></#if></#list><#elseif primaryKey?? && primaryKey.config?? && primaryKey.config.query?? && primaryKey.config.query.enabled><@buildFieldCond primaryKey/></#if><#list normalFields as p><@buildFieldCond p/></#list>;
        }
        if (otherCond != null && !otherCond.isEmpty()) {
            cond.andIfNeed(otherCond);
        }
        Where where = Where.create(cond);
        if (orderBy != null && !orderBy.isEmpty()) {
            where.orderBy().orderBy(orderBy);
        }<#if (api.query?? && api.query.orderFields?? && api.query.orderFields?size > 0)> else {
            where<#list api.query.orderFields as orderField>
                .orderBy${(orderField.type?lower_case)?cap_first}(<#if (orderField.prefix!"")?length != 0>"${orderField.prefix!""}", </#if><#if (orderField.value!"")?contains(".")>${orderField.value!""}<#else>"${orderField.value!""}"</#if>)</#list>;
        }</#if>
        return Query.build(owner, dataSourceName, ${api.name?cap_first}VO.class)
                .where(where, true)
                .addExcludeField(excludedFields)
                .find(page);
    }</#if>

    <#if !api.view><#if !(api.settings??) || api.settings.enableRemove!true>@Override
    @Transaction
    public int remove${api.name?cap_first}(IDatabase owner, String dataSourceName, <#if multiPrimaryKey>${api.name?cap_first}PK id<#else>${primaryKey.type} ${primaryKey.name}</#if>) throws Exception {
        if (<#if multiPrimaryKey || !primaryKey.type?ends_with("String")><#if multiPrimaryKey>id<#else>${primaryKey.name}</#if> == null<#else>StringUtils.isBlank(<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>)</#if>) {
            throw new NullArgumentException("<#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>");
        }
        if (StringUtils.isNotBlank(dataSourceName)) {
            return owner.openSession(dataSourceName, session -> session.delete(${entityName}.class, <#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>));
        }
        return owner.openSession(session -> session.delete(${entityName}.class, <#if multiPrimaryKey>id<#else>${primaryKey.name}</#if>));
    }

    @Override
    @Transaction
    public int remove${api.name?cap_first}s(IDatabase owner, String dataSourceName, <#if multiPrimaryKey>${api.name?cap_first}PK[] ids<#else>${primaryKey.type}[] ${primaryKey.name}s</#if>) throws Exception {
        if (ArrayUtils.isEmpty(<#if multiPrimaryKey>ids<#else>${primaryKey.name}s</#if>)) {
            throw new NullArgumentException("<#if multiPrimaryKey>ids<#else>${primaryKey.name}s</#if>");
        }
        if (StringUtils.isNotBlank(dataSourceName)) {
            return owner.openSession(dataSourceName, session -> BatchUpdateOperator.parseEffectCounts(session.delete(${entityName}.class, <#if multiPrimaryKey>ids<#else>${primaryKey.name}s</#if>)));
        }
        return owner.openSession(session -> BatchUpdateOperator.parseEffectCounts(session.delete(${entityName}.class, <#if multiPrimaryKey>ids<#else>${primaryKey.name}s</#if>)));
    }</#if></#if>
}